#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "filter.h"
#include "wavelib/header/wauxlib.h"
#include <fstream>
#include "axisrangesetting.h"
#include <cfloat>
#include "loggerwrapper.h"

using namespace std;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow), mPlotting(false),
      mConnected(false), mPortName("COM1"), mPortSpeed("460800"),
      mConnectedLable(new QLabel("端口: 未连接")),
      mPacketLable(new QLabel("包数: 0")),
      mDistanceLable(new QLabel("距离: 0 m")), mDebugLable(new QLabel) {
    ui->setupUi(this);
    ui->splitter->setStretchFactor(0, 10);
    ui->splitter->setStretchFactor(1, 0);

    mLogger = LoggerWrapper::instance().getLogger();

    if (!QDir().exists(mSaveDir)) {
        QDir().mkpath(mSaveDir);
    }

    QIcon icon;
    icon.addFile(QString::fromUtf8(":/images/distance.png"), QSize(),
                 QIcon::Normal, QIcon::Off);
    auto tb = new QToolButton(this);
    tb->setMenu(ui->menu_5);
    tb->setPopupMode(QToolButton::InstantPopup);
    tb->setIcon(icon);
    tb->setToolTip("显示距离");
    ui->toolBar->insertWidget(ui->actionFilter, tb);
    ui->toolBar->insertSeparator(ui->actionFilter);

    ui->statusbar->setStyleSheet(
        QString("QStatusBar::item{border: 0px}")); // 设置不显示label的边框
    ui->statusbar->addPermanentWidget(mDebugLable);
    ui->statusbar->addPermanentWidget(mDistanceLable);
    ui->statusbar->addPermanentWidget(mPacketLable);
    ui->statusbar->addPermanentWidget(mConnectedLable);
    ui->textBrowser->setVisible(false);

    mPlots[0] = ui->rope0lma;
    mPlots[1] = ui->rope0lf;
    mPlots[2] = ui->rope1lma;
    mPlots[3] = ui->rope1lf;
    mPlots[4] = ui->rope2lma;
    mPlots[5] = ui->rope2lf;
    mPlots[6] = ui->rope3lma;
    mPlots[7] = ui->rope3lf;

    ui->horizontalScrollBar->setRange(0, 0);
    connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this,
            SLOT(onHorzScrollBarChanged(int)));
    connect(mPlots[0]->xAxis, SIGNAL(rangeChanged(QCPRange)), this,
            SLOT(onxAxisChanged(QCPRange)));

    enable_controls(); // 端口和模拟都可以点

    // 显示距离
    QActionGroup *actionGroup = new QActionGroup(this);
    actionGroup->setExclusive(true); // 设置为单选框模式
    QList<QAction *> actions = {ui->action1_m,  ui->action2_m,   ui->action5_m,
                                ui->action10_m, ui->action15_m,  ui->action25_m,
                                ui->action50_m, ui->action100_m, ui->actionAll};
    for (QAction *action : actions) {
        actionGroup->addAction(action);
    }
    actionGroup->actions().at(0)->setChecked(true);
    mSelDspDist = ui->action1_m;
    connect(actionGroup, SIGNAL(triggered(QAction *)), this,
            SLOT(onDisplayDistanceChanged(QAction *)));

    readCfg();
    setupPlot();

    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        connect(
            mPlots[i], &QCustomPlot::mouseMove, this,
            [=](QMouseEvent *event) { onMouseMoveInPlot(event, mPlots[i]); });

        connect(mPlots[i], &QCustomPlot::selectionChangedByUser, this,
                [=]() { onGraphSelection(mPlots[i]); });

        connect(mPlots[i], &QCustomPlot::legendDoubleClick, this,
                [=](QCPLegend *legend, QCPAbstractLegendItem *item) {
                    onLegendDoubleClick(legend, item, mPlots[i]);
                });
        mPlots[i]->plotLayout()->insertRow(0);

        QCPTextElement *title = nullptr;
        if (i % WAVE_NUM_PER_ROPE == 0) {
            title = new QCPTextElement(
                mPlots[i],
                QString("钢丝绳%1 LMA(%)").arg(i / WAVE_NUM_PER_ROPE + 1),
                QFont("宋体", 8));
        } else {
            title = new QCPTextElement(
                mPlots[i],
                QString("钢丝绳%1 LF(mV)").arg(i / WAVE_NUM_PER_ROPE + 1),
                QFont("宋体", 8));
        }
        mPlots[i]->plotLayout()->addElement(0, 0, title);

        mPlots[i]->installEventFilter(this);
    }

    mDisplayChannel.rope0lma = 1;
    mDisplayChannel.rope0lf = 1;

    mDisplayChannel.rope1lma = 1;
    mDisplayChannel.rope1lf = 1;

    mDisplayChannel.rope2lma = 1;
    mDisplayChannel.rope2lf = 1;

    mDisplayChannel.rope3lma = 1;
    mDisplayChannel.rope3lf = 1;

    connect(&mReplotTimer, SIGNAL(timeout()), this, SLOT(replot()));
    connect(&mTestTimer, SIGNAL(timeout()), this, SLOT(genTestData()));
}

MainWindow::~MainWindow() {
    closeCsvFile();
    saveCfg();
    if (mSerialWorker != nullptr) {
        delete mSerialWorker;
    }
    delete ui;
    delete mConnectedLable;
    delete mPacketLable;
    delete mDistanceLable;
    delete mDebugLable;
}

// 清空屏幕。构造函数与连接函数调用
void MainWindow::setupPlot() {
    for (int i = 0; i < HEADS * WAVE_NUM_PER_ROPE; i++) {
        mPlots[i]->clearItems();
        mPlots[i]->clearPlottables();

        mYRange[i].valid = false;
        mYShift[i].valid = false;
        mXShift[i / WAVE_NUM_PER_ROPE].valid = false;

        // mPlots[i]->setBackground(QColor(0xb0, 0xb0, 0xb0));

        mPlots[i]->yAxis->grid()->setPen(QPen(Qt::SolidLine));
        mPlots[i]->yAxis->grid()->setZeroLinePen(QPen(Qt::SolidLine));

        mPlots[i]->xAxis->grid()->setPen(QPen(Qt::SolidLine));
        mPlots[i]->xAxis->grid()->setZeroLinePen(QPen(Qt::SolidLine));
        mPlots[i]->xAxis->grid()->setSubGridVisible(true);
        mPlots[i]->xAxis->grid()->setSubGridPen(
            QPen(QColor(80, 80, 80, 255), 1, Qt::DotLine));

        // Used for higher performance (see QCustoplots[i] real time
        // example)
        // 不需要抗锯齿
        mPlots[i]->setNotAntialiasedElements(QCP::aeAll);

        mPlots[i]->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
        mPlots[i]->setInteraction(QCP::iSelectPlottables, true);
        mPlots[i]->setInteraction(QCP::iSelectLegend, true);
        mPlots[i]->axisRect()->setRangeDrag(Qt::Horizontal);
        mPlots[i]->axisRect()->setRangeZoom(Qt::Horizontal);
        // mPlots[i]->yAxis->setLabelFont(QFont("Calibri"));
        // mPlots[i]->yAxis2->setLabelFont(QFont("Calibri"));
        // mPlots[i]->yAxis->setLabel("LMA(%)");
        // mPlots[i]->yAxis2->setLabel("LF(mV)");
        // mPlots[i]->yAxis2->setVisible(true);
        mPlots[i]->xAxis->setRange(0, mDisplayNum, Qt::AlignRight);

        //生成时间刻度对象
        QSharedPointer<QCPAxisTickerMeter> meterTicker(
            new QCPAxisTickerMeter(mTicksPerM));
        mPlots[i]->xAxis->setTicker(meterTicker);

        mPlots[i]->legend->setVisible(true);
        mPlots[i]->legend->setSelectableParts(QCPLegend::spItems);

        // for (int j(0); j < WAVE_NUM_PER_ROPE; ++j) {
        //     if (j == 0) {
        //         mPlots[i]->addGraph();
        //     } else {
        //         mPlots[i]->addGraph(mPlots[i]->xAxis, mPlots[i]->yAxis2);
        //     }
        //     mPlots[i]->graph()->setPen(mWaveColor[j]);
        //     mPlots[i]->graph()->setName(mWaveName[j]);
        //     if (mPlots[i]->legend->item(j)) {
        //         mPlots[i]->legend->item(j)->setTextColor(mWaveColor[j]);
        //     }
        // }
        mPlots[i]->addGraph();
        mPlots[i]->graph()->setPen(mWaveColor[0]);
        if (i % WAVE_NUM_PER_ROPE == 0) {
            mPlots[i]->graph()->setName(mWaveName[0]);
        } else {
            mPlots[i]->graph()->setName(mWaveName[1]);
        }

        if (mPlots[i]->legend->item(0)) {
            mPlots[i]->legend->item(0)->setTextColor(mWaveColor[0]);
        }
    }
}

// 定时刷新plot
void MainWindow::replot() {
    ui->horizontalScrollBar->setRange(0, mDataNum);
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        mPlots[i]->yAxis->rescale(true);
        // mPlots[i]->yAxis2->rescale(true);
        mPlots[i]->xAxis->setRange(mDataNum, mDisplayNum, Qt::AlignRight);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
}

void MainWindow::on_actionTestStart_triggered() {
    if (mSelDspDist == ui->actionAll) {
        mSelDspDist->setChecked(false);
    }

    mTestTimer.start(TEST_DATA_INTERVAL);
    mReplotTimer.start(REPLOT_INTERVAL);
    disable_test_controls();
    mPlotting = true;

    mPreSaveToFile = mSaveToFile;
    mSaveToFile = true;

    openCsvFile(); // 使用当前日期时间创建csv文件

    connect(&mMsgParser, SIGNAL(newData(QStringList)), this,
            SLOT(onNewDataArrived(QStringList))); // 绘图
    connect(&mMsgParser, SIGNAL(newData(QStringList)), this,
            SLOT(saveStream(QStringList))); // 保存文件
}

// 模拟定时器处理函数 生成随机数据并绘图
void MainWindow::genTestData() {
    QString data("$");
    QRandomGenerator *generator = QRandomGenerator::global();
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        if (i % 2 == 0) {
            data += QString::number(generator->bounded(100.0)) + " ";
        } else {
            data += QString::number(generator->bounded(100.0) + 200) + " ";
        }
    }
    data.replace(data.length() - 1, 1, ';');

    // 组装数据触发绘图与在文本框中显示值
    mMsgParser.unpack(data.toLocal8Bit().data());
}

// 从串口收到数据并组装后触发。画图
void MainWindow::onNewDataArrived(QStringList newData) {
    static int dataNum(0);
    static int index(0);
    static int i(0);
    volatile bool you_shall_NOT_PASS(false);

    if (!mPlotting) {
        return;
    }

    /* When a fast baud rate is set (921kbps was the first to starts to
       bug), this method is called multiple times (2x in the 921k tests),
       so a flag is used to throttle
       TO-DO: Separate processes, buffer data (1) and process data (2) */
    while (you_shall_NOT_PASS) {
    }
    you_shall_NOT_PASS = true;

    QString str;
    dataNum = newData.size();
    for (i = 0; i < dataNum; i++) {
        if (index >= HEADS * WAVE_NUM_PER_ROPE) {
            QString msg = QString("收到的数据数：%1 > 波形数 %2")
                    .arg(dataNum)
                    .arg(HEADS * WAVE_NUM_PER_ROPE);
            mDebugLable->setText(msg);
            mLogger->error(msg);
            break;
        }
        if (i > 0) {
            str += " ";
        }
        str += newData[index];
        mPlots[index]->graph(0)->addData(mDataNum, newData[index].toDouble());
        index++;
    }

    ++mDataNum;
    index = 0;

    ui->textBrowser->append(QString::number(mDataNum) + ": " + str);
    mPacketLable->setText(QString("包数: %1").arg(mDataNum));
    mDistanceLable->setText(
        QString("距离: %1 m")
            .arg(QString::number((mDataNum - 1) / (double)mTicksPerM, 'f', 3)));
    you_shall_NOT_PASS = false;
}

void MainWindow::onHorzScrollBarChanged(int value) {
    if (qAbs(mPlots[0]->xAxis->range().center() - value) <= 1) {
        return;
    }
    mDisplayNum = mPlots[0]->xAxis->range().size();
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        mPlots[i]->xAxis->setRange(value, mDisplayNum, Qt::AlignCenter);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
}

void MainWindow::onxAxisChanged(QCPRange range) {
    ui->horizontalScrollBar->setValue(
        qRound(range.center())); // adjust position of scroll bar slider
    ui->horizontalScrollBar->setPageStep(
        qRound(range.size())); // adjust size of scroll bar slider
}

void MainWindow::on_actionTestPause_triggered() {
    mTestTimer.stop();
    mReplotTimer.stop();

    mSaveToFile = mPreSaveToFile;
    closeCsvFile();

    disconnect(&mMsgParser, SIGNAL(newData(QStringList)), this,
               SLOT(onNewDataArrived(QStringList)));
    disconnect(&mMsgParser, SIGNAL(newData(QStringList)), this,
               SLOT(saveStream(QStringList)));

    replot();

    mPlotting = false;
    enable_controls();
}

void MainWindow::on_actionConnect_triggered() {
    if (mSelDspDist == ui->actionAll) {
        mSelDspDist->setChecked(false);
    }

    Options *optionDlg = new Options(this);
    optionDlg->setOptions(mPortName, mPortSpeed, mSaveToFile);

    if (optionDlg->exec() != QDialog::Accepted) {
        delete optionDlg;
        return;
    }

    mPortName = optionDlg->portName();
    mPortSpeed = optionDlg->portSpeed();
    mSaveToFile = optionDlg->saveStreamFlag();
    delete optionDlg;

    int baudRate = mPortSpeed.toInt();

    if (mSerialWorker) {
        delete mSerialWorker;
    }

    mSerialWorker = new SerialWorker(this);
    // mSerialPort->setBaudRate(baudRate);
    // mSerialPort->setParity(QSerialPort::NoParity);
    // mSerialPort->setDataBits(QSerialPort::Data8);
    // mSerialPort->setStopBits(QSerialPort::OneStop);
    if (mSerialWorker->openSerialPort(mPortName, baudRate)) {
        mConnectedLable->setText(QString("%1: 已连接").arg(mPortName));
        ui->textBrowser->clear();
        disable_port_controls();

        mConnected = true;
        on_actionStart_triggered();
    } else {
        mConnectedLable->setText(QString("%1: 连接失败").arg(mPortName));
    }
}

void MainWindow::on_actionDisconnect_triggered() {
    if (!mConnected) {
        return;
    }

    delete mSerialWorker;
    mSerialWorker = nullptr;

    mConnected = false;

    enable_controls();
    mConnectedLable->setText("端口: 未连接");
}

void MainWindow::on_actionShowTextBrowse_triggered() {
    if (ui->textBrowser->isVisible()) {
        ui->textBrowser->setVisible(false);
        ui->actionShowTextBrowse->setText("显示文本框");
    } else {
        ui->textBrowser->setVisible(true);
        ui->actionShowTextBrowse->setText("隐藏文本框");
    }
}

// 实时保存数据到csv文件
void MainWindow::saveStream(QStringList newData) {
    if (mSaveToFile && mCsvFile) {
        QTextStream out(mCsvFile);
        foreach (const QString &str, newData) { out << str << ","; }
        out << "\n";
    }
}

// 在状态栏显示鼠标的坐标值
void MainWindow::onMouseMoveInPlot(QMouseEvent *event, QCustomPlot *plot) {
    int xx = int(plot->xAxis->pixelToCoord(event->x()));
    double yy = plot->yAxis->pixelToCoord(event->y());

    auto it = std::find(std::begin(mPlots), std::end(mPlots), plot);
    if (it == std::end(mPlots)) {
        return;
    }
    int index = std::distance(std::begin(mPlots), it);

    QString coordinates = QString("钢丝绳%1  X:%2  %3:%4")
                              .arg(index / WAVE_NUM_PER_ROPE + 1)
                              .arg(xx)
                              .arg(index % WAVE_NUM_PER_ROPE ? "LF" : "LMA")
                              .arg(yy, 0, 'f', 2);

    ui->statusbar->showMessage(coordinates);
}

// 端口连接成功且记录按钮按下时调用
void MainWindow::openCsvFile(void) {
    mCsvFile = new QFile(
        mSaveDir +
        QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-") +
        "data-out.csv");
    if (!mCsvFile)
        return;
    if (!mCsvFile->open(QIODevice::ReadWrite | QIODevice::Text))
        return;
}

// 析构与端口关闭时调用
void MainWindow::closeCsvFile(void) {
    if (!mCsvFile)
        return;

    mCsvFile->close();
    if (mCsvFile)
        delete mCsvFile;
    mCsvFile = nullptr;
}

// 点了模拟的测试按钮之后各按钮的状态
void MainWindow::disable_test_controls() {
    ui->actionTestStart->setEnabled(false);
    ui->actionTestPause->setEnabled(true);

    ui->actionConnect->setEnabled(false);
    ui->actionDisconnect->setEnabled(false);
    ui->actionStart->setEnabled(false);
    ui->actionStop->setEnabled(false);
    ui->actionPause->setEnabled(false);
    ui->actionLoadFile->setEnabled(false);
}

// 端口已连接
void MainWindow::disable_port_controls() {
    ui->actionConnect->setEnabled(false);
    ui->actionDisconnect->setEnabled(true);
    ui->actionStart->setEnabled(true);
    ui->actionStop->setEnabled(false);
    ui->actionPause->setEnabled(false);

    ui->actionTestStart->setEnabled(false);
    ui->actionTestPause->setEnabled(false);
    ui->actionLoadFile->setEnabled(false);
}

// 端口未连接
void MainWindow::enable_controls() {
    ui->actionTestStart->setEnabled(true);
    ui->actionTestPause->setEnabled(false);

    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
    ui->actionStart->setEnabled(false);
    ui->actionStop->setEnabled(false);
    ui->actionPause->setEnabled(false);

    ui->actionLoadFile->setEnabled(true);
}

void MainWindow::on_actionAbout_triggered() {
    QString compileDate;
    QFileInfo fileInfo(QCoreApplication::applicationFilePath());
    if (fileInfo.exists()) {
        compileDate = fileInfo.lastModified().toString("yyyy-MM-dd hh:mm:ss");
    }

    QString aboutText =
        QString("版本: %1\n日期: %2")
            .arg(QApplication::applicationVersion(), compileDate);

    QMessageBox msgBox(this);
    msgBox.setWindowTitle("关于");
    msgBox.setText(aboutText);
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.button(QMessageBox::Ok)->setText("确定");

    msgBox.exec();
}

void MainWindow::onDisplayDistanceChanged(QAction *action) {
    static const QList<QAction *> actions = {
        ui->action1_m,  ui->action2_m,   ui->action5_m,
        ui->action10_m, ui->action15_m,  ui->action25_m,
        ui->action50_m, ui->action100_m, ui->actionAll};
    static const int index2Dis[] = {1, 2, 5, 10, 15, 25, 50, 100};

    int index = actions.indexOf(action);
    if (index == -1) {
        return;
    }

    mSelDspDist = action;
    if (index == actions.size() - 1) {
        if (mDataNum > 0) {
            mDisplayNum = mDataNum;
        }
    } else {
        mDisplayNum = mTicksPerM * index2Dis[index];
    }

    double centerX = mPlots[0]->xAxis->range().center();

    // replot();

    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        mPlots[i]->yAxis->rescale(true);
        // mPlots[i]->yAxis2->rescale(true);
        if (action == ui->actionAll) {
            mPlots[i]->xAxis->setRange(mDataNum, mDisplayNum, Qt::AlignRight);
        } else {
            mPlots[i]->xAxis->setRange(centerX, mDisplayNum, Qt::AlignCenter);
        }
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
    ui->horizontalScrollBar->setRange(0, mDataNum);
}

void MainWindow::onGraphSelection(QCustomPlot *plot) {
    for (int i = 0; i < plot->graphCount(); i++) {
        QCPGraph *graph = plot->graph(i);
        QCPPlottableLegendItem *item = plot->legend->itemWithPlottable(graph);
        if (item->selected() || graph->selected()) {
            item->setSelected(true);
            graph->setSelection(QCPDataSelection(graph->data()->dataRange()));
        }
    }
}

void MainWindow::onLegendDoubleClick(QCPLegend *legend,
                                     QCPAbstractLegendItem *item,
                                     QCustomPlot *plot) {
    Q_UNUSED(legend);

    if (!item) {
        return;
    }

    QCPPlottableLegendItem *plItem =
        qobject_cast<QCPPlottableLegendItem *>(item);

    if (plItem->plottable()->visible()) {
        plItem->plottable()->setVisible(false);
    } else {
        plItem->plottable()->setVisible(true);
    }
    plot->replot(QCustomPlot::rpQueuedReplot);
}

void MainWindow::init() {
    setupPlot(); // 清空屏幕
    mDataNum = 0;

    ui->textBrowser->clear();
    mDebugLable->setText("");
    mPacketLable->setText("包数: 0");
    mDistanceLable->setText("距离: 0 m");
}

void MainWindow::on_actionLoadFile_triggered() {
    QString fileName =
        QFileDialog::getOpenFileName(this, "打开文件", "", "csv文件 (*.csv)");

    if (fileName.isEmpty()) {
        return;
    }

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox msgBox(this);
        msgBox.setText("文件打开失败!");
        msgBox.exec();
        return;
    }

    on_actionDisconnect_triggered();
    init();

    QTextStream in(&file);
    while (!in.atEnd()) {
        QString line = in.readLine();
        line.chop(1); // 删除最后一个字符','

        QStringList list = line.split(",");
        auto num = list.size();
        if (num != HEADS * WAVE_NUM_PER_ROPE) {
            QMessageBox msgBox(this);
            msgBox.setText("文件数据错误!");
            msgBox.exec();
            break;
        }

        for (int i(0); i < num; ++i) {
            mPlots[i]->graph(0)->addData(mDataNum, list[i].toDouble());
        }
        ++mDataNum;
    }

    file.close();

    ui->actionAll->setChecked(true);
    mSelDspDist = ui->actionAll;
    mDisplayNum = mDataNum;
    replot();

    mPacketLable->setText(QString("包数: %1").arg(mDataNum));
    mDistanceLable->setText(
        QString("距离: %1 m")
            .arg(QString::number((mDataNum - 1) / (double)mTicksPerM, 'f', 3)));
}

// 是否是支持的小波
bool MainWindow::checkWaveName(const QString &wave) {
    // bior、rbior后缀集合
    static const QSet<QString> validSuffixes = {
        "1.1", "1.3", "1.5", "2.2", "2.4", "2.6", "2.8", "3.1",
        "3.3", "3.5", "3.7", "3.9", "4.4", "5.5", "6.8"};

    // 检查特定的字符串
    if (wave == "haar" || wave == "meyer") {
        return true;
    }

    bool ok;
    int num;
    // 检查以 "db" 开头的字符串
    if (wave.startsWith("db")) {
        num = wave.midRef(2).toInt(&ok);
        return ok && num >= 1 && num <= 38;
    }

    // 检查以 "sym" 开头的字符串
    if (wave.startsWith("sym")) {
        num = wave.midRef(3).toInt(&ok);
        return ok && num >= 2 && num <= 20;
    }

    // 检查以 "coif" 开头的字符串
    if (wave.startsWith("coif")) {
        num = wave.midRef(4).toInt(&ok);
        return ok && num >= 1 && num <= 17;
    }

    // 检查以 "bior" 开头的字符串
    if (wave.startsWith("bior") && wave.size() == QString("bior1.1").size()) {
        return validSuffixes.contains(wave.mid(4));
    }

    // 检查以 "rbior" 开头的字符串
    if (wave.startsWith("rbior") && wave.size() == QString("rbior1.1").size()) {
        return validSuffixes.contains(wave.mid(5));
    }

    // 如果都不匹配，则返回 false
    return false;
}

void MainWindow::on_actionFilter_triggered() {
    auto dataNum = mPlots[0]->graph(0)->dataCount();
    if (dataNum == 0) {
        QMessageBox::information(this, "提示", "没有数据！");
        return;
    }

    Filter *filterDlg = new Filter(this);
    filterDlg->setOptions(mWavelet, mDecLayerNum);
    if (filterDlg->exec() != QDialog::Accepted) {
        delete filterDlg;
        return;
    }
    mWavelet = filterDlg->waveletName().trimmed();
    mDecLayerNum = filterDlg->deCompLayer();
    if (!checkWaveName(mWavelet)) {
        delete filterDlg;
        QMessageBox::critical(this, "错误", "小波名称错误，请重新输入。");
        return;
    }
    delete filterDlg;

    double *inp = new double[dataNum];
    double *oup = new double[dataNum];
    denoise_object obj =
        denoise_init(dataNum, mDecLayerNum, mWavelet.toLocal8Bit().constData());

    for (int i = 0; i < HEADS * WAVE_NUM_PER_ROPE; i++) {
        // for (int j(0); j < WAVE_NUM_PER_ROPE; ++j) {
        int cur(0);
        auto data = mPlots[i]->graph(0)->data();
        QVector<double> index;
        for (const auto &one : *data) {
            index.append(one.mainKey());
            inp[cur++] = one.mainValue();
        }

        denoise(obj, inp, oup);

        // if (mPlots[i]->graphCount() <= j + WAVE_NUM_PER_ROPE) {
        // if (j == 0) {
        if (mPlots[i]->graphCount() == 1) {
            mPlots[i]->addGraph();
        }
        // } else {
        //     mPlots[i]->addGraph(mPlots[i]->xAxis, mPlots[i]->yAxis2);
        // }
        mPlots[i]->graph()->setPen(mWaveColor[1]);
        if (i % WAVE_NUM_PER_ROPE == 0) {
            mPlots[i]->graph()->setName(mWaveName[2]);
        } else {
            mPlots[i]->graph()->setName(mWaveName[3]);
        }
        if (mPlots[i]->legend->item(1)) {
            mPlots[i]->legend->item(1)->setTextColor(mWaveColor[1]);
        }
        // }

        QVector<double> filterOut(dataNum);
        std::copy(oup, oup + dataNum, filterOut.begin());
        mPlots[i]->graph(1)->setData(index, filterOut, true); // (key, value)
        mPlots[i]->yAxis->rescale(true);
        mPlots[i]->xAxis->setRange(dataNum, mDisplayNum, Qt::AlignRight);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
        // }
    }

    denoise_free(obj);
    delete[] inp;
    delete[] oup;
}

// 端口连接后才能点此按钮
void MainWindow::on_actionStart_triggered() {
    if (!mPlotting) {
        mSerialWorker->write("start\n");
        init();
        connect(mSerialWorker, SIGNAL(newData(QStringList)), this,
                SLOT(onNewDataArrived(QStringList)));
        connect(mSerialWorker, SIGNAL(newData(QStringList)), this,
                SLOT(saveStream(QStringList)));

        if (mSaveToFile) {
            openCsvFile(); // 使用当前日期时间创建csv文件
        }
        mPlotting = true;
    }
    ui->actionStart->setEnabled(false);
    ui->actionStop->setEnabled(true);
    ui->actionPause->setEnabled(true);
    mReplotTimer.start(REPLOT_INTERVAL);
}

void MainWindow::on_actionStop_triggered() {
    mSerialWorker->write("stop\n");
    ui->actionStart->setEnabled(true);
    ui->actionPause->setEnabled(false);
    ui->actionStop->setEnabled(false);

    closeCsvFile();
    disconnect(mSerialWorker, SIGNAL(newData(QStringList)), this,
               SLOT(onNewDataArrived(QStringList)));
    disconnect(mSerialWorker, SIGNAL(newData(QStringList)), this,
               SLOT(saveStream(QStringList)));

    mPlotting = false;
    mReplotTimer.stop();
    mReceivedData.clear();
    replot();
}

void MainWindow::on_actionPause_triggered() {
    mReplotTimer.stop();
    ui->actionStart->setEnabled(true);
    ui->actionPause->setEnabled(false);
}

void MainWindow::readCfg() {
    ifstream file(mCfgFile.toStdString());
    if (!file) {
        return;
    }

    YAML::Node doc = YAML::Load(file);
    for (const auto &serial : doc["serial"]) {
        string key = serial.first.as<string>();
        string value = serial.second.as<string>();
        if (key == "port") {
            mPortName = QString::fromStdString(value).toUpper();
        } else if (key == "baud") {
            mPortSpeed = QString::fromStdString(value);
        }
    }

    for (const auto &filter : doc["filter method"]) {
        string key = filter.first.as<string>();
        string value = filter.second.as<string>();
        if (key == "wave") {
            mWavelet = QString::fromStdString(value).toLower();
        } else if (key == "layer") {
            mDecLayerNum = QString::fromStdString(value).toInt();
        }
    }

    string save2file = doc["save to file"].as<string>();
    mSaveToFile =
        QString::fromStdString(save2file).toUpper() == "TRUE" ? true : false;

    string saveDays = doc["save latest days"].as<string>();
    mSaveDays = QString::fromStdString(saveDays).toInt();

    QHash<QString, QAction *> hash = {
        {"1m", ui->action1_m},   {"2m", ui->action2_m},
        {"5m", ui->action5_m},   {"10m", ui->action10_m},
        {"15m", ui->action15_m}, {"25m", ui->action25_m},
        {"50m", ui->action50_m}, {"100m", ui->action100_m},
        {"all", ui->actionAll}};
    string displayDistance = doc["display distance"].as<string>();
    QString key = QString::fromStdString(displayDistance).toLower();
    QAction *action;
    if (hash.contains(key)) {
        action = hash.value(key);
    } else {
        action = ui->actionAll;
    }
    onDisplayDistanceChanged(action);
    action->setChecked(true);
    mSelDspDist = action;
}

void MainWindow::saveCfg() {
    QFile file(mCfgFile);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out << "serial:\n";
        out << "    port: " << mPortName.toLower() << "\n";
        out << "    baud: " << mPortSpeed << "\n";

        out << "save to file: " << (mSaveToFile ? "true" : "false") << "\n";
        out << "save latest days: " << mSaveDays << "\n";

        const QList<QAction *> actions = {
            ui->action1_m,  ui->action2_m,   ui->action5_m,
            ui->action10_m, ui->action15_m,  ui->action25_m,
            ui->action50_m, ui->action100_m, ui->actionAll};
        const QString index2Dis[] = {"1m",  "2m",  "5m",   "10m", "15m",
                                     "25m", "50m", "100m", "all"};
        int i(0);
        for (; i < actions.size(); ++i) {
            if (actions[i]->isChecked()) {
                break;
            }
        }
        QString displayDistance;
        if (i == actions.size()) {
            displayDistance = "all";
        } else {
            displayDistance = index2Dis[i];
        }
        out << "display distance: " << displayDistance << "\n";

        out << "filter method:\n";
        out << "    wave: " << mWavelet << "\n";
        out << "    layer: " << mDecLayerNum << "\n";

        file.close();
    }
}

void MainWindow::on_actionChannel_triggered() {
    WaveDisplay *waveDisplayDlg = new WaveDisplay();
    waveDisplayDlg->setDisplayChannel(mDisplayChannel);
    if (waveDisplayDlg->exec() != QDialog::Accepted) {
        delete waveDisplayDlg;
        return;
    }

    mDisplayChannel = waveDisplayDlg->getDisplayChannel();
    delete waveDisplayDlg;

    mDisplayChannel.rope0lma ? mPlots[0]->show() : mPlots[0]->hide();
    mDisplayChannel.rope0lf ? mPlots[1]->show() : mPlots[1]->hide();

    mDisplayChannel.rope1lma ? mPlots[2]->show() : mPlots[2]->hide();
    mDisplayChannel.rope1lf ? mPlots[3]->show() : mPlots[3]->hide();

    mDisplayChannel.rope2lma ? mPlots[4]->show() : mPlots[4]->hide();
    mDisplayChannel.rope2lf ? mPlots[5]->show() : mPlots[5]->hide();

    mDisplayChannel.rope3lma ? mPlots[6]->show() : mPlots[6]->hide();
    mDisplayChannel.rope3lf ? mPlots[7]->show() : mPlots[7]->hide();
}

void MainWindow::on_actionEnlarge_triggered() {
    auto range = mPlots[0]->xAxis->range();
    mSelDspDist->setChecked(false);
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        if (i != 0) {
            mPlots[i]->xAxis->setRange(range);
        }
        mPlots[i]->xAxis->scaleRange(0.5);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
}

void MainWindow::on_actionReduce_triggered() {
    auto range = mPlots[0]->xAxis->range();
    mSelDspDist->setChecked(false);
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        if (i != 0) {
            mPlots[i]->xAxis->setRange(range);
        }
        mPlots[i]->xAxis->scaleRange(2);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
    if (event->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        QCustomPlot *clickedPlot = qobject_cast<QCustomPlot *>(obj);
        if (clickedPlot &&
            clickedPlot->rect().contains(
                clickedPlot->mapFromGlobal(mouseEvent->globalPos()))) {
            auto it =
                std::find(std::begin(mPlots), std::end(mPlots), clickedPlot);
            int index = std::distance(std::begin(mPlots), it);

            if (mSelectedPlotIdx != -1) {
                mPlots[mSelectedPlotIdx]->setBackground(QBrush(Qt::white));
                mPlots[mSelectedPlotIdx]->replot(QCustomPlot::rpQueuedReplot);
            }
            clickedPlot->setBackground(QBrush(Qt::lightGray));
            clickedPlot->replot(QCustomPlot::rpQueuedReplot);
            mSelectedPlotIdx = index;

            // 处理点击事件...
            // return true; // 事件已处理，不需要进一步传递
        }
    }
    // 如果不是我们需要处理的事件，或者我们已经处理了事件但希望它继续传递，
    // 则调用基类的事件过滤器（如果有的话）
    return QMainWindow::eventFilter(obj, event);
}

void MainWindow::mousePressEvent(QMouseEvent *event) {
    if (mSelectedPlotIdx != -1) {
        mPlots[mSelectedPlotIdx]->setBackground(QBrush(Qt::white));
        mPlots[mSelectedPlotIdx]->replot(QCustomPlot::rpQueuedReplot);
        mSelectedPlotIdx = -1;
    }

    QMainWindow::mousePressEvent(event);
}

void MainWindow::getYrange(int index) {
    if (mYRange[index].valid) {
        return;
    }
    for (int i = 0; i < mPlots[index]->graphCount(); ++i) {
        QCPGraph *graph = mPlots[index]->graph(i);
        if (graph) {
            bool valid(false);
            auto range = graph->getValueRange(valid);
            if (valid) {
                if (!mYRange[index].valid) {
                    mYRange[index].valid = valid;
                    mYRange[index].lower = range.lower;
                    mYRange[index].upper = range.upper;
                } else {
                    mYRange[index].lower =
                        qMin(mYRange[index].lower, range.lower);
                    mYRange[index].upper =
                        qMax(mYRange[index].upper, range.upper);
                }
            }
        }
    }
}

void MainWindow::on_actionYrange_triggered() {
    if (mSelectedPlotIdx == -1) {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("请先选中一个图表！");
        msgBox.setIcon(QMessageBox::Warning); // 设置图标
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.button(QMessageBox::Ok)->setText("确定");
        msgBox.exec();
        return;
    }

    double lower = mYRange[mSelectedPlotIdx].lower;
    double upper = mYRange[mSelectedPlotIdx].upper;
    if (!mYRange[mSelectedPlotIdx].valid) {
        getYrange(mSelectedPlotIdx);
        if (mYRange[mSelectedPlotIdx].valid) {
            lower = mYRange[mSelectedPlotIdx].lower;
            upper = mYRange[mSelectedPlotIdx].upper;
        } else {
            auto range = mPlots[mSelectedPlotIdx]->yAxis->range();
            lower = range.lower;
            upper = range.upper;
        }
    }

    QString text("%");
    if (mSelectedPlotIdx % WAVE_NUM_PER_ROPE) {
        text = "mV";
    }

    AxisRangeSetting *rangeDlg = new AxisRangeSetting(this);
    rangeDlg->setOption(text, lower, upper);

    if (rangeDlg->exec() != QDialog::Accepted) {
        delete rangeDlg;
        return;
    }
    auto range = rangeDlg->getOption();
    delete rangeDlg;

    mYRange[mSelectedPlotIdx].valid = true;
    mYRange[mSelectedPlotIdx].lower = range.first;
    mYRange[mSelectedPlotIdx].upper = range.second;

    mPlots[mSelectedPlotIdx]->yAxis->setRange(range.first, range.second);
    mPlots[mSelectedPlotIdx]->replot(QCustomPlot::rpQueuedReplot);
}

bool MainWindow::graphSelected() {
    if (mSelectedPlotIdx == -1) {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("请先选中一个图表！");
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.button(QMessageBox::Ok)->setText("确定");
        msgBox.exec();
        return false;
    }
    if (mPlots[mSelectedPlotIdx]->graphCount() == 0 ||
        mPlots[mSelectedPlotIdx]->graph(0)->dataCount() == 0) {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("没有数据！");
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.button(QMessageBox::Ok)->setText("确定");
        msgBox.exec();
        return false;
    }
    return true;
}

void MainWindow::on_actionYShift_triggered() {
    if (!graphSelected()) {
        return;
    }

    if (!mYShift[mSelectedPlotIdx].valid) {
        bool valid = false;
        auto range = mPlots[mSelectedPlotIdx]->graph(0)->getValueRange(valid);
        if (!valid) {
            return;
        }
        mYShift[mSelectedPlotIdx].step = (range.upper - range.lower) / 20;
        mYShift[mSelectedPlotIdx].valid = true;
        auto graph = mPlots[mSelectedPlotIdx]->graph(0);
        for (int i(0); i < graph->dataCount(); ++i) {
            mYShift[mSelectedPlotIdx].origin.push_back(
                graph->data()->at(i)->value);
        }
    }

    YShiftSetting *shiftDlg = new YShiftSetting(this);
    shiftDlg->setOption(mYShift[mSelectedPlotIdx], mPlots[mSelectedPlotIdx],
                        &mYShift[mSelectedPlotIdx].origin);

    if (shiftDlg->exec() == QDialog::Rejected) {
        //        YShiftSetting::shiftData(mPlots[mSelectedPlotIdx],
        //        &mYshift[mSelectedPlotIdx].origin,
        //                                 mYshift[mSelectedPlotIdx].offset);
        delete shiftDlg;
        return;
    }
    auto option = shiftDlg->getOption();
    mYShift[mSelectedPlotIdx].offset = option.first;
    mYShift[mSelectedPlotIdx].step = option.second;
    YShiftSetting::shiftData(mPlots[mSelectedPlotIdx],
                             &mYShift[mSelectedPlotIdx].origin,
                             mYShift[mSelectedPlotIdx].offset);
    delete shiftDlg;
}

void MainWindow::on_actionSum_triggered() {
    if (!graphSelected()) {
        return;
    }

    auto graph = mPlots[mSelectedPlotIdx]->graph(0);
    auto size = graph->dataCount();
    if (size < 2) {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("数据个数小于2！");
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.button(QMessageBox::Ok)->setText("确定");
        msgBox.exec();
        return;
    }

    auto xAxisRange = mPlots[mSelectedPlotIdx]->xAxis->range();
    double keyLower = graph->data()->at(0)->key;
    double keyUpper = graph->data()->at(size - 1)->key;
    double dotPerM = (size - 1) / (keyUpper - keyLower);

    double maxVal = -DBL_MAX;
    double minVal = DBL_MAX;
    double right = -DBL_MAX;
    double left = DBL_MAX;
    double start =
        qMax(keyLower, xAxisRange.lower) - keyLower; // 坐标值 偏移到下标0
    double end = qMin(keyUpper, xAxisRange.upper) - keyLower;
    for (int i = start * dotPerM; i <= end; ++i) { // i数据的下标
        if (i < 0) {
            continue;
        }
        if (i >= size) {
            break;
        }
        double val = graph->data()->at(i)->mainValue(); // 获取y值
        if (val > maxVal) {
            maxVal = val;
            right = graph->data()->at(i)->mainKey();
        } else if (val < minVal) {
            minVal = val;
            left = graph->data()->at(i)->mainKey();
        }
    }

    QString title =
        QString("统计 钢丝绳%1 %2")
            .arg(mSelectedPlotIdx / WAVE_NUM_PER_ROPE + 1)
            .arg(mSelectedPlotIdx % WAVE_NUM_PER_ROPE ? "LF(mV)" : "LMA(%)");

    QString maxMin;
    if (left != DBL_MAX && right != -DBL_MAX) {
        maxMin = QString("最大值  %1  在 %2m\n最小值  %3  在 %4m\n")
                     .arg(maxVal, 0, 'f', 2)
                     .arg(right / mTicksPerM, 0, 'f', 2)
                     .arg(minVal, 0, 'f', 2)
                     .arg(left / mTicksPerM, 0, 'f', 2);
    }
    QString length = QString("视窗总长    %1m(%2-%3)\n钢丝绳总长  %4m")
                         .arg((end - start) / mTicksPerM, 0, 'f', 2)
                         .arg((start + keyLower) / mTicksPerM, 0, 'f', 2)
                         .arg((end + keyLower) / mTicksPerM, 0, 'f', 2)
                         .arg((keyUpper - keyLower) / mTicksPerM, 0, 'f', 2);

    QMessageBox msgBox(this);
    msgBox.setWindowTitle(title);
    msgBox.setText(maxMin + length);
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.button(QMessageBox::Ok)->setText("确定");
    msgBox.exec();
}

void MainWindow::showLegend(bool show) {
    for (int i(0); i < HEADS * WAVE_NUM_PER_ROPE; ++i) {
        mPlots[i]->legend->setVisible(show);
        mPlots[i]->replot(QCustomPlot::rpQueuedReplot);
    }
}

void MainWindow::on_actionLegend_triggered() {
    if (mPlots[0]->legend->visible()) {
        showLegend(false);
        ui->actionLegend->setText("显示图例");
    } else {
        showLegend(true);
        ui->actionLegend->setText("隐藏图例");
    }
}

void MainWindow::on_actionXShift_triggered() {
    if (!graphSelected()) {
        return;
    }
    int index = mSelectedPlotIdx / WAVE_NUM_PER_ROPE;
    if (!mXShift[index].valid) {
        mXShift[index].valid = true;
        mXShift[index].start = 0;
        mXShift[index].length = (mDataNum - 1) / (double)mTicksPerM;
        mXShift[index].originLen = mXShift[index].length;
    }
    XShiftSetting *shiftDlg = new XShiftSetting(this);
    shiftDlg->setOption(mXShift[index], mPlots[index * WAVE_NUM_PER_ROPE],
                        mPlots[index * WAVE_NUM_PER_ROPE + 1]);
    if (shiftDlg->exec() == QDialog::Rejected) {
        delete shiftDlg;
        return;
    }
    auto option = shiftDlg->getOption();
    mXShift[index].start = option.first;
    mXShift[index].length = option.second;
    delete shiftDlg;
    double start = option.first * mTicksPerM;
    double factor = option.second / mXShift[index].originLen;
    XShiftSetting::shiftData(mPlots[index * WAVE_NUM_PER_ROPE],
                             mPlots[index * WAVE_NUM_PER_ROPE + 1], start,
                             factor);
}
